//
//  SFCLib+Internals.h
//  SVNForCocoa
//
//  Created by Jeremy Pereira on 07/11/2013.
//  Copyright (c) 2013 Jeremy Pereira. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <svn_client.h>
#import <svn_props.h>
#import <apr_pools.h>
#import "SFCAPRPool.h"
#import "SFCClient.h"
#import "SFCError.h"
#import "SFCRevision.h"
#import "SFCCredentials.h"
#import "SFCLock.h"
#import "SFCCommitItem.h"
/*!
 *    @file
 *    @brief Interfaces used internally but not really for public consumption.
 *
 *    <p>
 *    These are things needed by the SFCLib but should not be required by
 *    public users.  This is mainly to keep the normal public interface clean
 *    with respect to the APR and svn C libraries.
 *    </p><p>
 *  
 *    </p>
 */

/*!
 *    @brief Internal interface for SFCAPRPool
 *
 *    Allows us to see into the APR underlying the pool object.
 */
@interface SFCAPRPool(SFCLibExtensions)
/*!
 *    @brief The APR pool that the object wraps.
 */
@property (nonatomic, readonly, assign) apr_pool_t* aprPool;

@end

/*!
 *    @brief Internal interface for SFCClient.
 */
@interface SFCClient (SFCLibExtensions)

@property (readonly, assign) svn_client_ctx_t* svnClientContext;

@end

/*!
 *    @brief Internal SFCError methods
 */
@interface SFCError (SFCLibExtensions)

/*!
 *    @brief Create an NSError from an svn_error_t
 *
 *    The code will be the APR error code and the domain will be 
 *    SVN_ERROR_DOMAIN if the code <= SVN_ERR_LAST, otherwise we guess a domain
 *    based on the error code.
 *
 *    @param svnError The subversion error object
 *
 *    @return A new NSError.
 *    @todo Handle domain selection better than code <= SVN_ERR_LAST => SVN_ERROR_DOMAIN
 */
+(NSError*) svnError: (svn_error_t*) svnError;

/*!
 *    @brief Create an svn error from the NSError.
 *
 *    This does its best to wrap an NSError with an svn_error_t.  If the error
 *    domain is SVN_ERROR_DOMAIN, it copies the code as the apr error, otherwise
 *    it adds calculates a new code > SVN_ERR_LAST based on the error domain, so
 *    we can recover the domain.
 *
 *    @param anError An NSError
 *
 *    @return An svn_error_t
 *    @todo In order to make sure the message string doesn't get deallocated, 
 *          we retain the NSError in a collection.  This means it is currently
 *          leaking.  We should clear the errors out at the end of the public
 *          API call.
 *    @todo Implement an algorithm to handle the domain better.
 */
+(svn_error_t*) svnErrorFromError: (NSError*) anError;

@end

/*!
 *    @brief Internal declarations for SFCRevision
 */
@interface SFCRevision (SFCLibExtensions)
/*!
 *    @brief Get the svn revision kind of this revision
 */
@property (readonly, assign) enum svn_opt_revision_kind kind;

/*!
 *    @brief Get an SVN opt revision from this revision.
 *
 */
@property (readonly, assign) const svn_opt_revision_t* svnOptRevision;


@end
/*!
 *    @brief Internal declaration for SFCRevisionRange
 */
@interface SFCRevisionRange(SFCLibExtensions)
/*!
 *    @brief Get us an svn_revision_range from this revision range.
 */
@property (readonly, assign) const svn_opt_revision_range_t* svnRevisionRange;

@end

/*!
 *    @brief This category is used internally to add utility functionality to
 *           NSString.
 */
@interface NSString (SFCLibExtensions)

/*!
 *    @brief Return a new svn_string_t representation of the current string.
 *
 *    The svn_string_t is made from the UTF8 representation of self.
 *
 *    @param pool SFC Pool to allocate the memory from.
 *
 *    @return An svn_string_t.
 */
-(svn_string_t*) svnStringWithPool: (SFCAPRPool*) pool;

/*!
 *    @brief Return a new svn_string_t representation of the current string.
 *
 *    The svn_string_t is made from the UTF8 representation of self.
 *
 *    @param pool APR Pool to allocate the memory from.
 *
 *    @return An svn_string_t.
 */
-(svn_string_t*) svnStringWithAprPool: (apr_pool_t*) pool;

/*!
 *    @brief Turns a relative path into an absolute path.
 *
 *    @return Absolute path of ther relative path.
 */
-(NSString*) absolutePath;

/*!
 *    @brief Create an NSString from an svn string
 *
 *    @param svnString String to reate a new NSString from.
 *
 *    @return a new NSString.
 */
+(NSString*) stringWithSvnString: (const svn_string_t*) svnString;

@end

/*!
 *    @brief Internal methods for SFCCommitInfo
 */
@interface SFCCommitInfo (SFCLibExtensions)

/*!
 *    @brief Designated initialiser.
 *
 *    Initialises the object freom the svn_commit_info
 *
 *    @param svnCommitInfo The svn commit info.
 *
 *    @return A new SFCCommitInfo
 */
-(id) initWithSvnCommitInfo: (const svn_commit_info_t*) svnCommitInfo;

@end

/*!
 *    @brief Internal interface for SFCCommitItem
 *
 */
@interface SFCCommitItem (SFCLibExtensions)

/*!
 *    @brief Initialise a SFCCommitItem from an svn structure.
 *
 *    @param commitItem The svn object to initialise from.
 *
 *    @return An inititialsed commit item.
 *    @todo The initialiser doesn't populate all the fields.
 */
-(id) initWithSvnCommitItem: (const svn_client_commit_item3_t*) commitItem;

@end

/*!
 *    @brief Internal methods for an SVN log entry.
 *
 */
@interface SFCLogEntry(SFCLibExtensions)

/*!
 *    @brief Initialise a log entry from the svn struct.
 *
 *    @param svnLogEntry The svn struct to get the info from
 *
 *    @return and SFCLogEntry
 */
-(id) initWithSvnLogEntry: (const svn_log_entry_t*) svnLogEntry;

@end
/*!
 *    @brief Internal declarations for SFCWorkingCopyInfo
 */
@interface SFCWorkingCopyInfo (SFCLibExtensions)
/*!
 *    @brief Initialise a working copy info oject from the svn struct.
 *
 *    @param svnWorkingCopyInfo SVN struct to initialise from.
 *
 *    @return A new working copy info object.
 *    @todo Needs implementing properly.
 */
-(id) initWithSvnWorkingCopyInfo: (const svn_wc_info_t*) svnWorkingCopyInfo;

@end

/*!
 *    @brief Internal methods for an svn client info.
 */
@interface SFCClientInfo (SFCLibExtensions)
/*!
 *    @brief Initialise a client info from its corresponding SVN struct.
 *
 *    @param svnInfo Client info from svn client library.
 *
 *    @return A new client info object.
 */
-(id) initWithSvnInfo: (const svn_client_info2_t*) svnInfo;

@end
/*!
 *    @brief Internal methods for a log changed path.
 */
@interface SFCLogChangedPath (SFCLibExtensions)

/*!
 *    @brief Create a new log changed path from the svn object.
 *
  *    @param svnChangedPath svn changed path to use.
 *
 *    @return A new log changed path.
 */
-(id) initWithSvnLogChangedPath: (const svn_log_changed_path2_t*) svnChangedPath;

@end

/*!
 *    @brief Internal methods for a status entry.
 */
@interface SFCStatusEntry (SFCLibExtensions)

/*!
 *    @brief Initialise a status entry from the equivalent SVN struct.
 *
 *    @param svnStatus svn struct to take dat from.
 *
 *    @return A new status entry.
 *    @todo WE are only populationg a couple of the properties at the moment.
 */
-(id) initWithSvnStatusEntry: (const svn_client_status_t*) svnStatus;

@end

/*!
 *    @brief Internal methods for a working copy notification.
 */
@interface SFCWCNotification (SFCLibExtensions)
/*!
 *    @brief Initialise from an SVN notify struct.
 *
 *    @param svnNotification SVN notification struct.
 *
 *    @return A new notification
 *    @todo Initialisation does not yet populate any of the properties
 */
-(id) initWithSvnNotification: (const svn_wc_notify_t*) svnNotification;

@end

/*!
 *    @brief Internal methods for credentials.
 */
@interface SFCCredentials (SFCLibExtensions)

/*!
 *    @brief Compares the kind of this set of credentials against a credentials
 *           kind.
 *
 *    Throws an excception if the credentials are the wrong kind.
 *
 *    @param expectedKind What this set of credentials should be for.
 *
 *    @todo We need unit tests for the kind check.
 */
-(void) validateKind: (NSString*) expectedKind;

/*!
 *    @brief Provide the credentials for SVN.
 *
 *    Returns an object that SVN understands containing the credentials.  The
 *    object must be capable of outliving the SFCCredentials, so should be
 *    allocated from the provided pool.
 *
 *    @param aprPool Pool to allocate memory for the svn object.
 *    @return A pointer to the SVN object.
 */
-(void*) svnCredentialsWithAprPool: (apr_pool_t*) aprPool;

/*!
 *    @brief Create an SFCCredentials object from the content of the given 
 *           pointer.
 *
 *    The pointer must be compatible with the given kind or unpredictable
 *    results may occur.
 *
 *    @param kind           The kind of credentials we are creating.
 *    @param svnCredentials A pointer to an svn_credentials object.
 *
 *    @return A new set of SFCCredentials.
 */
+(SFCCredentials*) credentialsForKind: (NSString*) kind
                       svnCredentials: (const void*) svnCredentials;


@end

/*!
 *    @brief Internal declarations for SFCLock
 */
@interface SFCLock (SFCLibExtensions)

/*!
 *    @brief Initialise a new lock object based on the given svn lock.
 *    @param svnLock The SVN lock structure.
 *
 *    @return A new lock
 *    @todo Needs implementing properly.
 */
-(id) initWithSvnLock: (const svn_lock_t*) svnLock;

@end

/*!
 *    @brief Internal methods for property inherited items
 */
@interface SFCPropertyInheritedItem (SFCLibExtensions)
/*!
 *    @brief Initialsise a property inherited item from its SVN counterpart.
 *
 *    @param svnPit SVN property inherited item.
 *
 *    @return A new SFCPropertyInheritedItem
 */
-(id) initWithSvnPropertyInheritedItem: (const svn_prop_inherited_item_t*) svnPit;

@end

/*!
 *    @brief Declaration of a block that can convert an svn type e.g. 
 *    svn_log_entry_t to an Objective-C type
 *
 *    @param svnObject An svn lib object pointer cast as void*
 *    @param size size of the object if known, or 0 if not.
 *    @return an Objerctive-C object.
 */
typedef id (^MakeObjectFromSvnType)(const void* svnObject, size_t size);

/*!
 *    @brief Convenience methods for NSDictionaries and APR hashes.
 */
@interface NSDictionary (SFCLibExtensions)

/*!
 *    @brief Create a dictionary from an APR hash
 *
 *    Creates and returns an NSDictioanry from an APR hash using the supplied 
 *    blocks to convert the keys and values to Objectice-C objects.  Ig the hash
 *    is null, returns an empty dictionary.
 *
 *    @param aprHash        Hash from which to create a dictionary.
 *    @param keyConverter   Converter for the keys.  Must return an object that
 *                          confoirms to NSCopying because it will be used as a 
 *                          key in an NSDictionary.
 *    @param valueConverter Converter for the values
 *
 *    @return An NSDictionary.
 */
+(NSDictionary*) dictionaryWithAprHash: (apr_hash_t*) aprHash
                          keyConverter: (MakeObjectFromSvnType) keyConverter
                        valueConverter: (MakeObjectFromSvnType) valueConverter ;


/*!
 *    @brief Create a dictionary from an APR hash.
 *
 *    The keys are assumed to be const char* null terminated stringds.  The 
 *    values are assumed to be svn_string_t*s.
 *
 *    @param aprHash Hash of key/value pairs to convert.
 *
 *    @return A Dictionary of NSString/NSString key value pairs.
 */
+(NSDictionary*) dictionaryWithAprHashStringToSVNString: (apr_hash_t*) aprHash;
/*!
 *    @brief Create an APR hash from the receiver using the given pool for all
 *           memory allocations.
 *
 *    @param keyConverter    Block to convert keys in the dictionary to APR
 *                           compatible keys.
 *    @param objectConverter Block to convert the objects in the NSDictionary to
 *                           APR compatible objects.
 *    @param pool            Pool to make all allocations from.
 *
 *    @return An APR hash with the same keys and values as the dictionary.
 */
-(apr_hash_t*) aprHashWithKeyConverter: (void (^)(id keyIn, const void** keyOut, apr_ssize_t* keyLength, SFCAPRPool* pool)) keyConverter
                       objectConverter: (void (^)(id objectIn, const void** objectOut, SFCAPRPool* pool)) objectConverter
                                  pool: (SFCAPRPool*) pool;
/*!
 *    @brief Make an apr hash of C string => svn_string_t.
 *
 *    @param pool                Pool to llocate meory from.  The hash and all
 *                               of its keys and values will be allocated from
 *                               here.
 *
 *    @return an apr hash.
 */
-(apr_hash_t*) aprHashOfUTF8StringsWithPool: (SFCAPRPool*) pool;

@end

/*!
 *    @brief Extensions to alow NSArray to manipulate apr arrays.
 */
@interface NSArray (SFCLibExtensions)

/*!
 *    @brief Create an NSArray from an apr array
 *
 *    @param aprArray        apr array to create array from.
 *    @param objectConverter Converter to convert the APR array elements to 
 *                           objects.  Note that the object converter will be 
 *                           passed a pointer to the objects in the array, so
 *                           if it's an svn_log_entry_t* in the array, the object
 *                           converter should expect svn_log_entry_t**.
 *
 *    @return NSArray mirroring the input array.
 */
+(NSArray*) arrayWithAprArray: (const apr_array_header_t*) aprArray
              objectConverter: (MakeObjectFromSvnType) objectConverter;

/*!
 *    @brief Create an APR array from an NSArray.
 *
 *    @param elementSize     Size of the elements that go in the APR array.
 *    @param objectConverter A block that can convert the objects in the array
 *                           to objects suitable for putting in tha APR array.
 *                           destination is a pointer to the apr array element
 *                           object is the object to convert and pool is the 
 *                           pool out of which to allocate memory.
 *    @param destination     Parameter of objectConverter that points to the 
 *                           new element in the apr array.
 *    @param object          Parameter in objectConverter that is the object to
 *                           be converted.
 *    @param pool            Memory pool to allocate APR objects out of
 *
 *    @return An apr array allocated form the given pool.
 */
-(apr_array_header_t*) aprArrayWithSize: (size_t) elementSize
                        objectConverter: (void (^)(void* destination, id object, SFCAPRPool* pool)) objectConverter
                                   pool: (SFCAPRPool*) pool;
/*!
 *    @brief Make an array of UTF-8 strings.
 *
 *   This is a convenience method for when this array is known to contain 
 *   NSStrings.
 *
 *    @param pool Pool to allocate memory from.  Both the array header and the
 *                string contents are allocated from here.
 *
 *    @return An apr array.
 */
-(apr_array_header_t*) aprArrayOfUTF8StringsWithPool: (SFCAPRPool*) pool;

@end

/*!
 *    @brief Extensions to convert apr_time_t values.
 *
 *    apr_time_t is a time in microseconds since Jan 1970.
 */
@interface NSDate (SFCLibExtensions)

/*!
 *    @brief Create an NSDate from an apr_time_t
 *
 *    @param aprTime THe time to convert to an NSDate
 *
 *    @return The NSDate.
 */
+(NSDate*) dateWithAprTime: (apr_time_t) aprTime;

/*!
 *    @brief The apr_time_t representation of this date.
 */
@property (nonatomic, readonly, assign) apr_time_t aprTime;

@end

/*!
 *    @brief Internal extensions to NSOutputStream
 */
@interface NSOutputStream(SFCLibExtensions)
/*!
 *    @brief Create an sv_stream_t from this stream.
 *
 *    @param pool Pool from which to allocate memory.
 *
 *    @return An svn_stream_t
 */
-(svn_stream_t*) svnStreamWithPool: (SFCAPRPool*) pool;

@end

/*!
 *    @brief Internal extensions to SFCDiffOptions
 */
@interface SFCDiffOptions (SFCLibExtensions)
/*!
 *    @brief Get an svn_diff_file_options struct.
 *
 *    The returned struct has the same lifetime as the object.
 *
 *    @return an svn_diff_file_options struct.
 */
-(svn_diff_file_options_t*) svnDiffFileOptions;

@end

/*!
 *    @brief Internal SFCDirectoryEntry interface.
 */
@interface SFCDirectoryEntry (SFCLibExtensions)
/*!
 *    @brief Initialise from an svn_dir_ent_t.
 *
 *    @param svnDirEnt THe svn struct to initialise from.
 *
 *    @return An initialised SFCDirectoryEntry.
 */
-(id) initWithSvnDirEnt: (const svn_dirent_t*) svnDirEnt;

@end

/*!
 *    @brief Internal declarations for SFCClientCopySource
 */
@interface SFCClientCopySource (SFCLibExtensions)
/*!
 *    @brief An svn struct that represents the copy source.  
 *
 *    This has the same lifetime as the object that vends it.
 */
@property (nonatomic, readonly, assign) svn_client_copy_source_t* svnClientCopySource;

@end

/*!
 *    @brief Internal declarations for SFCIODirectoryEntry
 */
@interface SFCIODirectoryEntry (SFCLibExtensions)

/*!
 *    @brief Initialise with the equivalent SVN struct.
 *
 *    @param dirEnt struct to init from.
 *
 *    @return An SFCIODirectoryEntry.
 */
-(id) initWithSvnIODirEnt: (const svn_io_dirent2_t*) dirEnt;

@end

/*!
 *    @brief Describes a conflict that needs resolving.
 */
@interface SFCConflictDescription (SFCLibExtensions)

/*!
 *    @brief Initislie the conflict description from an SVN conflict description.
 *
 *    @param svnDescription svn conflict description.
 *
 *    @return A new SFCConflictDescription
 */
-(id) initWithSvnDescription: (const svn_wc_conflict_description2_t*) svnDescription;

@end
/*!
 *    @brief Encapsulates a conflict resolution decision.
 */
@interface SFCConflictResult (SFCLibExtensions)

/*!
 *    @brief Get an SVN conflict result allocated from the given pool.
 *
 *    @param pool Pool to allocate memory from.
 *
 *    @return The conflict result.
 */
-(svn_wc_conflict_result_t*) svnConflictResultWithAprPool: (apr_pool_t*) pool;

@end

@interface SFCConflictVersion (SFCLibExtensions)

-(id) initWithSvnVersion: (const svn_wc_conflict_version_t*) svnVersion;

@end


